import type { Ref } from 'vue';
import type { GetFileChunkWithSHA256 } from '@/util/file_type';
import CryptoJS from 'crypto-js';
import type { file_mergeType } from '@/api/newfile/file_type';

// GetFileChunkWithSHA256 获取文件分片处理之后的信息
export async function GetFileChunkWithSHA256(
  file: File,
  chunkSize: number,
  processVal: Ref<number>
): Promise<GetFileChunkWithSHA256> {
  const result: GetFileChunkWithSHA256 = {
    FileName: file.name,
    FileSize: 0,
    ChunkSize: chunkSize,
    FileSha256: '',
    FileChunkHash: new Map<number, string>(),
    FileChunk: []
  };

  const fileChunkTotal: number = Math.ceil(file.size / chunkSize);

  const sha256Obj = CryptoJS.algo.SHA256.create();

  result.FileSize = file.size;

  let start = 0; // 分片开始位置
  let end = 0;
  let index: number = 1;
  while (start < result.FileSize) {
    end = start + chunkSize;
    if (end > result.FileSize) {
      end = result.FileSize;
    }

    // 单个分片
    const chunk: Blob = file.slice(start, end);
    // 存储分片
    result.FileChunk.push(chunk);

    // 计算单个 chunk 的哈希值
    await new Promise((resolve, reject) => {
      const fileReader = new FileReader();
      fileReader.onload = () => {
        try {
          // chunk 数据转 WordArray
          const arrayBuffer = fileReader.result as ArrayBuffer;
          const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer);
          sha256Obj.update(wordArray); // 文件总体hash更新
          const chunkMD5Obj = CryptoJS.algo.MD5.create(); // 创建分片的 md5 对象
          chunkMD5Obj.update(wordArray); // 分片的 md5 更新
          const chunkHashVal: string = chunkMD5Obj.finalize().toString(CryptoJS.enc.Hex); // 分片的哈希值
          result.FileChunkHash.set(index, chunkHashVal); // 保存分片哈希结果
          resolve(null);
        } catch (error) {
          console.log('计算分片哈希值失败 ', error);
          reject(error);
        }
      };
      fileReader.onerror = (event) => {
        reject(new Error(`文件分片读取失败: ${(event.target as any).error}`));
      };

      // 读取文件分片
      fileReader.readAsArrayBuffer(chunk);
    });
    processVal.value = parseFloat((index / fileChunkTotal).toFixed(2));
    index++;
    start = end;
  }
  // 文件总体哈希值计算
  result.FileSha256 = sha256Obj.finalize().toString(CryptoJS.enc.Hex);
  return result;
}

export interface UploadChunkParam {
  startUpload: (data: any) => Promise<any>;
  uploadFunc: (data: FormData) => Promise<any>;
  mergeFunc: (data: file_mergeType) => Promise<any>;
  fileDataInfo: GetFileChunkWithSHA256;
  processVal: Ref<number>;
}

// Upload 或其分片上传地址并上传分片文件数据
export async function UploadChunk(req: UploadChunkParam): Promise<void> {
  // 1.上传准备请求
  const startResp = await req.startUpload({
    file_sha256: req.fileDataInfo.FileSha256, //哈希值
    file_size: req.fileDataInfo.FileSize, //文件大小
    split_count: req.fileDataInfo.FileChunk.length, //分片数量
    chunk_size: req.fileDataInfo.ChunkSize // 分片的大小
  });
  if (startResp.data.skip === true) {
    console.log('上传成功');
    console.log(startResp);
    req.processVal.value = 1;
    return startResp;
  }
  // 上传准备响应
  // {
  //   "skip": false,
  //   "upload_id": ""   minio uploadid
  // }
  // 2.分片上传请求
  let index: number = 1;
  const chunk_md5 = [] as any; //处理返回的数据
  for (let i = 0; i < req.fileDataInfo.FileChunk.length; i++) {
    const chunkData = req.fileDataInfo.FileChunk[i];
    const a = new FormData();
    a.set('file_sha256', req.fileDataInfo.FileSha256);
    a.set('upload_id', startResp.data.upload_id);
    a.set('file_chunk', chunkData);
    a.set('chunk_seq', (i + 1).toString());
    const uploadResp = await req.uploadFunc(a);
    // 上传分片
    // {
    //   "etag": ""
    // }
    const etag: string = uploadResp.data.etag;
    const md5 = req.fileDataInfo.FileChunkHash.get(i + 1);
    if (etag !== md5) {
      console.log(etag, md5);
      throw new Error('服务端返回的分片 md5 和已有的不同,数据校验错误');
    }
    const obj = {
      part_number: i + 1,
      etag: uploadResp.data.etag
    };
    chunk_md5.push(obj);
    req.processVal.value = parseFloat((index / req.fileDataInfo.FileChunk.length).toFixed(2));
    index++;
  }
  // 3.文件合并请求
  const margeData: file_mergeType = {
    file_sha256: req.fileDataInfo.FileSha256, //哈希值
    upload_id: startResp.data.upload_id, //上传id
    file_name: req.fileDataInfo.FileName, //文件名称
    chunk_data: chunk_md5 //
  };
  // 文件合并响应
  // {
  //   "file_sha256": ""
  // }
  const result = await req.mergeFunc(margeData);
  return result;
}
